//
/*   Helper code for Matrix * Vector operation program
*    Copyright (C) 2009 Goffredo Marocchi
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <time.h>
#include <string.h>
#include <cutil_inline.h>
#include <cuda.h>
#include <cutil_math.h>
#include <vector_types.h>

#include "IGGS_CUDA_defines.h"
#include "MatTest_helper.h"

////////////////////////////////////////////////////
/////CPU, data initialization
////////////////////////////////////////////////////

/** \brief General data initialization function.
*
*	This function takes one matrix and two vectors as inputs, allocates space for them in RAM
*	and initializes them.\n The matrix is set to an Identity matrix with the first four elements of the 4th column set to
*	the translation vector (1,0,0,1). The source vector is set to all 1's and the result vector is zeroed.
*
*	\param matA reference to the source matrix we want to allocate (RAM/System memory)
*			and initialize, float*.
*	\param vecB reference to the source vector we want to allocate (RAM/System memory)
*			and initialize, float*.
*	\param h_C_CPU reference to the result vector (C/C++ generated) we want to allocate (RAM/System memory)
*			and initialize, float*
*	\return void
*	\sa DATA_SZ, DATA_V, N_EL, ROWS, COLS, P_MATC, PRINT_MAT_DIM, PRINT_CVEC_DIM
*/
extern "C" void init_host_data_ (float* matA, float* vecB, float* h_C_CPU) {

	send_msg("Initializing data...\n");

	//PRINT_POINTER (*matA);
	//PRINT_POINTER (*vecB);

	for(int i = 0; i < ROWS; i++){

		for(int j = 0; j < COLS; j++) {

			if (j == i) MATC(matA,i,j,COLS) = 1.0f;
			else MATC(matA,i,j,COLS) = 0.0f;

			vecB[j] = 1.0f;
			h_C_CPU[j] = 0.0f;

		}

	}

	//let's do a test 4x4 case first in CUDA then with the CUBLAS libs.

	MATC(matA,0,3,COLS) = 1.0f;
	MATC(matA,1,3,COLS) = 0.0f;
	MATC(matA,2,3,COLS) = 0.0f;
	MATC(matA,3,3,COLS) = 1.0f;

	//PRINT_POINTER (*matA);

	PRINT_MAT_DIM (ROWS, COLS);
	PRINT_CVEC_DIM (N_EL);

	PRINTLN;

	return;
}

/** \brief Times and runs the Mat*Vec test on the CPU (C/C++).
*
*	This function launches STEPS times the mat_vec() function which executes the multiplication
*	of source matrix matA and source vector vecB storing the result in the result vector h_C_CPU.
*
*	\param matA reference to the source matrix (RAM/System memory),
*			float*.
*	\param vecB reference to the source vector (RAM/System memory),
*			float*.
*	\param h_C_CPU reference to the result vector (RAM/System memory),
*			float*.
*	\return the time it takes to run this whole test (mat_vec() executed STEPS times), float.
*	\sa STEPS, ROWS, COLS, start_timer(), stop_timer(), mat_vec()
*/

extern "C" float execute_test_CPU_ ( float *matA, float *vecB, float *h_C_CPU)
{
	float time = 0.0f;
	unsigned int timer = 0;

	start_timer (&timer);

	for (int n = 0; n < STEPS; n++) {

		mat_vec(matA, ROWS, COLS, vecB, h_C_CPU);

	}

	stop_timer (timer, &time);

	send_msg ("\n\nExec time only on CPU: %f (ms)\n\n", time);

	return time;
}


/** \brief Software Mat*Vec operation implementation (C/C++).
*
*	The mat_vec() efficiently executes the multiplication
*	of source matrix a and source vector b storing the result in the result vector c.
*
*	\param a reference to the column major ordered source matrix (RAM/System memory),
*			float*.
*	\param b reference to the source vector (RAM/System memory),
*			float*.
*	\param c reference to the result vector (RAM/System memory),
*			float*.
*	\param R Number of Rows of the source Matrix, const int
*	\param C Number of Columns of the source Matrix, const int
*	\return void
*
*/
void mat_vec ( float *a, const int R, const int C, float *b, float *c) {

	//A is a column major ordered matrix

	float temp = 0;

	if ( null == a || null == b || null == c ) return;

	for (int j = 0; j < C; j++) {
	//for (int i = 0; i < R; i++) {
		temp = b[j];

		for (int i = 0; i < R; i++) {
		//for (int j = 0; j < C; j++) {

			if ( j == 0) c[i] = 0;

			c[i] += MATC(a,i,j,R) * temp;
			//c[i] += MATC(a,i,j,R) * b[i];

		}

	}

	return;
}
